/*
 * pgp-pgsql.c
 *		PostgreSQL 的 pgp 封装。
 *
 * Copyright (c) 2005 Marko Kreen
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *	  notice, this list of 条件和以下免责声明。
 * 2. Redistributions in binary form must reproduce the above copyright
 *	  notice, this list of条件和以下免责声明在文档和/或其他提供的材料
 * 与分发一起。
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 或后果
 * 损害（包括但不限于替代商品或服务的采购；丧失使用、数据或利润；或业务中断）
 * 无论因何原因以及在任何责任理论下，无论是合同、严格
 * 责任或侵权行为（包括疏忽或其他）以任何方式产生
 * 出于对本软件的使用，即使在被告知有可能发生
 * 这样的损害。
 *
 * contrib/pgcrypto/pgp-pgsql.c
 */

#include "postgres.h"

#include "catalog/pg_type.h"
#include "common/string.h"
#include "funcapi.h"
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "mbuf.h"
#include "pgp.h"
#include "px.h"
#include "utils/array.h"
#include "utils/builtins.h"

/* 
 * 公共函数 
 */ 
PG_FUNCTION_INFO_V1(pgp_sym_encrypt_bytea);
PG_FUNCTION_INFO_V1(pgp_sym_encrypt_text);
PG_FUNCTION_INFO_V1(pgp_sym_decrypt_bytea);
PG_FUNCTION_INFO_V1(pgp_sym_decrypt_text);

PG_FUNCTION_INFO_V1(pgp_pub_encrypt_bytea);
PG_FUNCTION_INFO_V1(pgp_pub_encrypt_text);
PG_FUNCTION_INFO_V1(pgp_pub_decrypt_bytea);
PG_FUNCTION_INFO_V1(pgp_pub_decrypt_text);

PG_FUNCTION_INFO_V1(pgp_key_id_w);

PG_FUNCTION_INFO_V1(pg_armor);
PG_FUNCTION_INFO_V1(pg_dearmor);
PG_FUNCTION_INFO_V1(pgp_armor_headers);

/* 
 * 在没有转换或错误的情况下返回 src 
 */ 
static text * fc_convert_charset(text *fc_src, int fc_cset_from, int fc_cset_to)
{
	int			fc_src_len = VARSIZE_ANY_EXHDR(fc_src);
	unsigned char *fc_dst;
	unsigned char *fc_csrc = (unsigned char *) VARDATA_ANY(fc_src);
	text	   *fc_res;

	fc_dst = pg_do_encoding_conversion(fc_csrc, fc_src_len, fc_cset_from, fc_cset_to);
	if (fc_dst == fc_csrc)
		return fc_src;

	fc_res = cstring_to_text((char *) fc_dst);
	pfree(fc_dst);
	return fc_res;
}

static text * fc_convert_from_utf8(text *fc_src)
{
	return fc_convert_charset(fc_src, PG_UTF8, GetDatabaseEncoding());
}

static text * fc_convert_to_utf8(text *fc_src)
{
	return fc_convert_charset(fc_src, GetDatabaseEncoding(), PG_UTF8);
}

static void fc_clear_and_pfree(text *fc_p)
{
	px_memset(fc_p, 0, VARSIZE_ANY(fc_p));
	pfree(fc_p);
}

/* 
 * expect-* 参数存储 
 */ 
struct debug_expect
{
	int			debug;
	int			expect;
	int			cipher_algo;
	int			s2k_mode;
	int			s2k_count;
	int			s2k_cipher_algo;
	int			s2k_digest_algo;
	int			compress_algo;
	int			use_sess_key;
	int			disable_mdc;
	int			unicode_mode;
};

static void fc_fill_expect(struct debug_expect *fc_ex, int fc_text_mode)
{
	fc_ex->debug = 0;
	fc_ex->expect = 0;
	fc_ex->cipher_algo = -1;
	fc_ex->s2k_mode = -1;
	fc_ex->s2k_count = -1;
	fc_ex->s2k_cipher_algo = -1;
	fc_ex->s2k_digest_algo = -1;
	fc_ex->compress_algo = -1;
	fc_ex->use_sess_key = -1;
	fc_ex->disable_mdc = -1;
	fc_ex->unicode_mode = -1;
}

#define EX_MSG(arg) \
	ereport(NOTICE, (errmsg( \
		"pgp_decrypt: unexpected %s: expected %d got %d", \
		CppAsString(arg), fc_ex->arg, fc_ctx->arg)))

#define EX_CHECK(arg) do { \
		if (fc_ex->arg >= 0 && fc_ex->arg != fc_ctx->arg) EX_MSG(arg); \
	} while (0)

static void fc_check_expect(PGP_Context *fc_ctx, struct debug_expect *fc_ex)
{
	EX_CHECK(cipher_algo);
	EX_CHECK(s2k_mode);
	EX_CHECK(s2k_count);
	EX_CHECK(s2k_digest_algo);
	EX_CHECK(use_sess_key);
	if (fc_ctx->use_sess_key)
		EX_CHECK(s2k_cipher_algo);
	EX_CHECK(disable_mdc);
	EX_CHECK(compress_algo);
	EX_CHECK(unicode_mode);
}

static void fc_show_debug(const char *fc_msg)
{
	ereport(NOTICE, (errmsg("dbg: %s", fc_msg)));
}

static int fc_set_arg(PGP_Context *fc_ctx, char *fc_key, char *fc_val,
		struct debug_expect *fc_ex)
{
	int			fc_res = 0;

	if (strcmp(fc_key, "cipher-algo") == 0)
		fc_res = pgp_set_cipher_algo(fc_ctx, fc_val);
	else if (strcmp(fc_key, "disable-mdc") == 0)
		fc_res = pgp_disable_mdc(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "sess-key") == 0)
		fc_res = pgp_set_sess_key(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "s2k-mode") == 0)
		fc_res = pgp_set_s2k_mode(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "s2k-count") == 0)
		fc_res = pgp_set_s2k_count(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "s2k-digest-algo") == 0)
		fc_res = pgp_set_s2k_digest_algo(fc_ctx, fc_val);
	else if (strcmp(fc_key, "s2k-cipher-algo") == 0)
		fc_res = pgp_set_s2k_cipher_algo(fc_ctx, fc_val);
	else if (strcmp(fc_key, "compress-algo") == 0)
		fc_res = pgp_set_compress_algo(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "compress-level") == 0)
		fc_res = pgp_set_compress_level(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "convert-crlf") == 0)
		fc_res = pgp_set_convert_crlf(fc_ctx, atoi(fc_val));
	else if (strcmp(fc_key, "unicode-mode") == 0)
		fc_res = pgp_set_unicode_mode(fc_ctx, atoi(fc_val));

	/* 
 * 剩余的选项用于调试/测试，因此在面向用户的文档中没有记录。 
 */ 
	else if (fc_ex != NULL && strcmp(fc_key, "debug") == 0)
		fc_ex->debug = atoi(fc_val);
	else if (fc_ex != NULL && strcmp(fc_key, "expect-cipher-algo") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->cipher_algo = pgp_get_cipher_code(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-disable-mdc") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->disable_mdc = atoi(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-sess-key") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->use_sess_key = atoi(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-s2k-mode") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->s2k_mode = atoi(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-s2k-count") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->s2k_count = atoi(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-s2k-digest-algo") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->s2k_digest_algo = pgp_get_digest_code(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-s2k-cipher-algo") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->s2k_cipher_algo = pgp_get_cipher_code(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-compress-algo") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->compress_algo = atoi(fc_val);
	}
	else if (fc_ex != NULL && strcmp(fc_key, "expect-unicode-mode") == 0)
	{
		fc_ex->expect = 1;
		fc_ex->unicode_mode = atoi(fc_val);
	}
	else
		fc_res = PXE_ARGUMENT_ERROR;

	return fc_res;
}

/* 
 * 查找下一个单词。 将 ',' 和 '=' 视为单词。 跳过空白。 
 * 将单词信息放入 res_p, res_len。 
 * 返回指向下一个单词的指针。 
 */ 
static char * fc_getword(char *fc_p, char **fc_res_p, int *fc_res_len)
{
	/* 开头的空白 */ 
	while (*fc_p && (*fc_p == ' ' || *fc_p == '\t' || *fc_p == '\n'))
		fc_p++;

	/* 单词数据 */ 
	*fc_res_p = fc_p;
	if (*fc_p == '=' || *fc_p == ',')
		fc_p++;
	else
		while (*fc_p && !(*fc_p == ' ' || *fc_p == '\t' || *fc_p == '\n'
					   || *fc_p == '=' || *fc_p == ','))
			fc_p++;

	/* 单词结束 */ 
	*fc_res_len = fc_p - *fc_res_p;

	/* 结尾的空白 */ 
	while (*fc_p && (*fc_p == ' ' || *fc_p == '\t' || *fc_p == '\n'))
		fc_p++;

	return fc_p;
}

/* 
 * 转换为小写 asciiz 字符串。 
 */ 
static char * fc_downcase_convert(const uint8 *fc_s, int fc_len)
{
	int			fc_c,
				fc_i;
	char	   *fc_res = palloc(fc_len + 1);

	for (fc_i = 0; fc_i < fc_len; fc_i++)
	{
		fc_c = fc_s[fc_i];
		if (fc_c >= 'A' && fc_c <= 'Z')
			fc_c += 'a' - 'A';
		fc_res[fc_i] = fc_c;
	}
	fc_res[fc_len] = 0;
	return fc_res;
}

static int fc_parse_args(PGP_Context *fc_ctx, uint8 *fc_args, int fc_arg_len,
		   struct debug_expect *fc_ex)
{
	char	   *fc_str = fc_downcase_convert(fc_args, fc_arg_len);
	char	   *fc_key,
			   *fc_val;
	int			fc_key_len,
				fc_val_len;
	int			fc_res = 0;
	char	   *fc_p = fc_str;

	while (*fc_p)
	{
		fc_res = PXE_ARGUMENT_ERROR;
		fc_p = fc_getword(fc_p, &fc_key, &fc_key_len);
		if (*fc_p++ != '=')
			break;
		fc_p = fc_getword(fc_p, &fc_val, &fc_val_len);
		if (*fc_p == '\0')
			;
		else if (*fc_p++ != ',')
			break;

		if (*fc_key == 0 || *fc_val == 0 || fc_val_len == 0)
			break;

		fc_key[fc_key_len] = 0;
		fc_val[fc_val_len] = 0;

		fc_res = fc_set_arg(fc_ctx, fc_key, fc_val, fc_ex);
		if (fc_res < 0)
			break;
	}
	pfree(fc_str);
	return fc_res;
}

static MBuf *
fc_create_mbuf_from_vardata(text *fc_data)
{
	return mbuf_create_from_data((uint8 *) VARDATA_ANY(fc_data),
								 VARSIZE_ANY_EXHDR(fc_data));
}

static void fc_init_work(PGP_Context **fc_ctx_p, int fc_is_text,
		  text *fc_args, struct debug_expect *fc_ex)
{
	int			fc_err = pgp_init(fc_ctx_p);

	fc_fill_expect(fc_ex, fc_is_text);

	if (fc_err == 0 && fc_args != NULL)
		fc_err = fc_parse_args(*fc_ctx_p, (uint8 *) VARDATA_ANY(fc_args),
						 VARSIZE_ANY_EXHDR(fc_args), fc_ex);

	if (fc_err)
		px_THROW_ERROR(fc_err);

	if (fc_ex->debug)
		px_set_debug_handler(fc_show_debug);

	pgp_set_text_mode(*fc_ctx_p, fc_is_text);
}

static bytea * fc_encrypt_internal(int fc_is_pubenc, int fc_is_text,
				 text *fc_data, text *fc_key, text *fc_args)
{
	MBuf	   *fc_src,
			   *fc_dst;
#ifdef FDD //cppcheck
	uint8		fc_tmp[VARHDRSZ] = {'\0'};
#else
	uint8		fc_tmp[VARHDRSZ];
#endif
	uint8	   *fc_restmp;
	bytea	   *fc_res;
	int			fc_res_len;
	PGP_Context *fc_ctx;
	int			fc_err;
	struct debug_expect fc_ex;
	text	   *fc_tmp_data = NULL;

	fc_init_work(&fc_ctx, fc_is_text, fc_args, &fc_ex);

	if (fc_is_text && pgp_get_unicode_mode(fc_ctx))
	{
		fc_tmp_data = fc_convert_to_utf8(fc_data);
		if (fc_tmp_data == fc_data)
			fc_tmp_data = NULL;
		else
			fc_data = fc_tmp_data;
	}

	fc_src = fc_create_mbuf_from_vardata(fc_data);
	fc_dst = mbuf_create(VARSIZE_ANY(fc_data) + 128);

	/* 
 * 为头部保留空间 
 */ 
	mbuf_append(fc_dst, fc_tmp, VARHDRSZ);

	/* 
 * 设置密钥 
 */ 
	if (fc_is_pubenc)
	{
		MBuf	   *fc_kbuf = fc_create_mbuf_from_vardata(fc_key);

		fc_err = pgp_set_pubkey(fc_ctx, fc_kbuf,
							 NULL, 0, 0);
		mbuf_free(fc_kbuf);
	}
	else
		fc_err = pgp_set_symkey(fc_ctx, (uint8 *) VARDATA_ANY(fc_key),
							 VARSIZE_ANY_EXHDR(fc_key));

	/* 
 * 加密 
 */ 
	if (fc_err >= 0)
		fc_err = pgp_encrypt(fc_ctx, fc_src, fc_dst);

	/* 
 * 检查错误 
 */ 
	if (fc_err)
	{
		if (fc_ex.debug)
			px_set_debug_handler(NULL);
		if (fc_tmp_data)
			fc_clear_and_pfree(fc_tmp_data);
		pgp_free(fc_ctx);
		mbuf_free(fc_src);
		mbuf_free(fc_dst);
		px_THROW_ERROR(fc_err);
	}

	/* res_len 包括 VARHDRSZ */ 
	fc_res_len = mbuf_steal_data(fc_dst, &fc_restmp);
	fc_res = (bytea *) fc_restmp;
	SET_VARSIZE(fc_res, fc_res_len);

	if (fc_tmp_data)
		fc_clear_and_pfree(fc_tmp_data);
	pgp_free(fc_ctx);
	mbuf_free(fc_src);
	mbuf_free(fc_dst);

	px_set_debug_handler(NULL);

	return fc_res;
}

static bytea * fc_decrypt_internal(int fc_is_pubenc, int fc_need_text, text *fc_data,
				 text *fc_key, text *fc_keypsw, text *fc_args)
{
	int			fc_err;
	MBuf	   *fc_src = NULL,
			   *fc_dst = NULL;
#ifdef FDD //cppcheck
	uint8		fc_tmp[VARHDRSZ] = {'\0'};
#else
	uint8		fc_tmp[VARHDRSZ];
#endif
	uint8	   *fc_restmp;
	bytea	   *fc_res;
	int			fc_res_len;
	PGP_Context *fc_ctx = NULL;
	struct debug_expect fc_ex;
	int			fc_got_unicode = 0;


	fc_init_work(&fc_ctx, fc_need_text, fc_args, &fc_ex);

	fc_src = mbuf_create_from_data((uint8 *) VARDATA_ANY(fc_data),
								VARSIZE_ANY_EXHDR(fc_data));
	fc_dst = mbuf_create(VARSIZE_ANY(fc_data) + 2048);

	/* 
 * 为头部保留空间 
 */ 
	mbuf_append(fc_dst, fc_tmp, VARHDRSZ);

	/* 
 * 设置密钥 
 */ 
	if (fc_is_pubenc)
	{
		uint8	   *fc_psw = NULL;
		int			fc_psw_len = 0;
		MBuf	   *fc_kbuf;

		if (fc_keypsw)
		{
			fc_psw = (uint8 *) VARDATA_ANY(fc_keypsw);
			fc_psw_len = VARSIZE_ANY_EXHDR(fc_keypsw);
		}
		fc_kbuf = fc_create_mbuf_from_vardata(fc_key);
		fc_err = pgp_set_pubkey(fc_ctx, fc_kbuf, fc_psw, fc_psw_len, 1);
		mbuf_free(fc_kbuf);
	}
	else
		fc_err = pgp_set_symkey(fc_ctx, (uint8 *) VARDATA_ANY(fc_key),
							 VARSIZE_ANY_EXHDR(fc_key));

	/* 解密 */ 
	if (fc_err >= 0)
	{
		fc_err = pgp_decrypt(fc_ctx, fc_src, fc_dst);

		if (fc_ex.expect)
			fc_check_expect(fc_ctx, &fc_ex);

		/* 记住设置 */ 
		fc_got_unicode = pgp_get_unicode_mode(fc_ctx);
	}

	mbuf_free(fc_src);
	pgp_free(fc_ctx);

	if (fc_err)
	{
		px_set_debug_handler(NULL);
		mbuf_free(fc_dst);
		px_THROW_ERROR(fc_err);
	}

	fc_res_len = mbuf_steal_data(fc_dst, &fc_restmp);
	mbuf_free(fc_dst);

	/* res_len 包括 VARHDRSZ */ 
	fc_res = (bytea *) fc_restmp;
	SET_VARSIZE(fc_res, fc_res_len);

	if (fc_need_text && fc_got_unicode)
	{
		text	   *fc_utf = fc_convert_from_utf8(fc_res);

		if (fc_utf != fc_res)
		{
			fc_clear_and_pfree(fc_res);
			fc_res = fc_utf;
		}
	}
	px_set_debug_handler(NULL);

	return fc_res;
}

/* 
 * 对称密钥函数的封装 
 */ 
Datum pgp_sym_encrypt_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_arg = PG_GETARG_BYTEA_PP(2);

	fc_res = fc_encrypt_internal(0, 0, fc_data, fc_key, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_arg, 2);
	PG_RETURN_TEXT_P(fc_res);
}

Datum pgp_sym_encrypt_text(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_arg = PG_GETARG_BYTEA_PP(2);

	fc_res = fc_encrypt_internal(0, 1, fc_data, fc_key, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_arg, 2);
	PG_RETURN_TEXT_P(fc_res);
}


Datum pgp_sym_decrypt_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_arg = PG_GETARG_BYTEA_PP(2);

	fc_res = fc_decrypt_internal(0, 0, fc_data, fc_key, NULL, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_arg, 2);
	PG_RETURN_TEXT_P(fc_res);
}

Datum pgp_sym_decrypt_text(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_arg = PG_GETARG_BYTEA_PP(2);

	fc_res = fc_decrypt_internal(0, 1, fc_data, fc_key, NULL, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_arg, 2);
	PG_RETURN_TEXT_P(fc_res);
}

/* 
 * 公钥函数的封装 
 */ 

Datum pgp_pub_encrypt_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_arg = PG_GETARG_BYTEA_PP(2);

	fc_res = fc_encrypt_internal(1, 0, fc_data, fc_key, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_arg, 2);
	PG_RETURN_TEXT_P(fc_res);
}

Datum pgp_pub_encrypt_text(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_arg = PG_GETARG_BYTEA_PP(2);

	fc_res = fc_encrypt_internal(1, 1, fc_data, fc_key, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_arg, 2);
	PG_RETURN_TEXT_P(fc_res);
}


Datum pgp_pub_decrypt_bytea(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_psw = NULL,
			   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_psw = PG_GETARG_BYTEA_PP(2);
	if (PG_NARGS() > 3)
		fc_arg = PG_GETARG_BYTEA_PP(3);

	fc_res = fc_decrypt_internal(1, 0, fc_data, fc_key, fc_psw, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_psw, 2);
	if (PG_NARGS() > 3)
		PG_FREE_IF_COPY(fc_arg, 3);
	PG_RETURN_TEXT_P(fc_res);
}

Datum pgp_pub_decrypt_text(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data,
			   *fc_key;
	text	   *fc_psw = NULL,
			   *fc_arg = NULL;
	text	   *fc_res;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_key = PG_GETARG_BYTEA_PP(1);
	if (PG_NARGS() > 2)
		fc_psw = PG_GETARG_BYTEA_PP(2);
	if (PG_NARGS() > 3)
		fc_arg = PG_GETARG_BYTEA_PP(3);

	fc_res = fc_decrypt_internal(1, 1, fc_data, fc_key, fc_psw, fc_arg);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_FREE_IF_COPY(fc_key, 1);
	if (PG_NARGS() > 2)
		PG_FREE_IF_COPY(fc_psw, 2);
	if (PG_NARGS() > 3)
		PG_FREE_IF_COPY(fc_arg, 3);
	PG_RETURN_TEXT_P(fc_res);
}


/* 
 * PGP ascii armor 的封装 
 */ 

/* 
 * pg_armor 的辅助函数。 将键和值的数组转换为 
 * 纯 C 数组，并检查它们是否包含无效字符。 
 */ 
static int fc_parse_key_value_arrays(ArrayType *fc_key_array, ArrayType *fc_val_array,
					   char ***fc_p_keys, char ***fc_p_values)
{
	int			fc_nkdims = ARR_NDIM(fc_key_array);
	int			fc_nvdims = ARR_NDIM(fc_val_array);
	char	  **fc_keys,
			  **fc_values;
	Datum	   *fc_key_datums,
			   *fc_val_datums;
	bool	   *fc_key_nulls,
			   *fc_val_nulls;
	int			fc_key_count,
				fc_val_count;
	int			fc_i;

	if (fc_nkdims > 1 || fc_nkdims != fc_nvdims)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("wrong number of array subscripts")));
	if (fc_nkdims == 0)
		return 0;

	deconstruct_array(fc_key_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_key_datums, &fc_key_nulls, &fc_key_count);

	deconstruct_array(fc_val_array,
					  TEXTOID, -1, false, TYPALIGN_INT,
					  &fc_val_datums, &fc_val_nulls, &fc_val_count);

	if (fc_key_count != fc_val_count)
		ereport(ERROR,
				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
				 errmsg("mismatched array dimensions")));

	fc_keys = (char **) palloc(sizeof(char *) * fc_key_count);
	fc_values = (char **) palloc(sizeof(char *) * fc_val_count);

	for (fc_i = 0; fc_i < fc_key_count; fc_i++)
	{
		char	   *fc_v;

		/* 检查密钥是否包含任何奇怪的内容 */ 
		if (fc_key_nulls[fc_i])
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for header key")));

		fc_v = TextDatumGetCString(fc_key_datums[fc_i]);

		if (!pg_is_ascii(fc_v))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("header key must not contain non-ASCII characters")));
		if (strstr(fc_v, ": "))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("header key must not contain \": \"")));
		if (strchr(fc_v, '\n'))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("header key must not contain newlines")));
		fc_keys[fc_i] = fc_v;

		/* 值也相同 */ 
		if (fc_val_nulls[fc_i])
			ereport(ERROR,
					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
					 errmsg("null value not allowed for header value")));

		fc_v = TextDatumGetCString(fc_val_datums[fc_i]);

		if (!pg_is_ascii(fc_v))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("header value must not contain non-ASCII characters")));
		if (strchr(fc_v, '\n'))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("header value must not contain newlines")));

		fc_values[fc_i] = fc_v;
	}

	*fc_p_keys = fc_keys;
	*fc_p_values = fc_values;
	return fc_key_count;
}

Datum pg_armor(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data;
	text	   *fc_res;
	int			fc_data_len;
	StringInfoData fc_buf;
	int			fc_num_headers;
	char	  **fc_keys = NULL,
			  **fc_values = NULL;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_data_len = VARSIZE_ANY_EXHDR(fc_data);
	if (PG_NARGS() == 3)
	{
		fc_num_headers = fc_parse_key_value_arrays(PG_GETARG_ARRAYTYPE_P(1),
											 PG_GETARG_ARRAYTYPE_P(2),
											 &fc_keys, &fc_values);
	}
	else if (PG_NARGS() == 1)
		fc_num_headers = 0;
	else
		elog(ERROR, "unexpected number of arguments %d", PG_NARGS());

	initStringInfo(&fc_buf);

	pgp_armor_encode((uint8 *) VARDATA_ANY(fc_data), fc_data_len, &fc_buf,
					 fc_num_headers, fc_keys, fc_values);

	fc_res = palloc(VARHDRSZ + fc_buf.len);
	SET_VARSIZE(fc_res, VARHDRSZ + fc_buf.len);
	memcpy(VARDATA(fc_res), fc_buf.data, fc_buf.len);
	pfree(fc_buf.data);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_RETURN_TEXT_P(fc_res);
}

Datum pg_dearmor(PG_FUNCTION_ARGS)
{
	text	   *fc_data;
	bytea	   *fc_res;
	int			fc_data_len;
	int			fc_ret;
	StringInfoData fc_buf;

	fc_data = PG_GETARG_TEXT_PP(0);
	fc_data_len = VARSIZE_ANY_EXHDR(fc_data);

	initStringInfo(&fc_buf);

	fc_ret = pgp_armor_decode((uint8 *) VARDATA_ANY(fc_data), fc_data_len, &fc_buf);
	if (fc_ret < 0)
		px_THROW_ERROR(fc_ret);
	fc_res = palloc(VARHDRSZ + fc_buf.len);
	SET_VARSIZE(fc_res, VARHDRSZ + fc_buf.len);
	memcpy(VARDATA(fc_res), fc_buf.data, fc_buf.len);
	pfree(fc_buf.data);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_RETURN_TEXT_P(fc_res);
}

/* pgp_armor_headers 的交叉调用状态 */ 
typedef struct
{
	int			nheaders;
	char	  **keys;
	char	  **values;
} pgp_armor_headers_state;

Datum pgp_armor_headers(PG_FUNCTION_ARGS)
{
	FuncCallContext *fc_funcctx;
	pgp_armor_headers_state *fc_state;
	char	   *fc_utf8key;
	char	   *fc_utf8val;
	HeapTuple	fc_tuple;
	TupleDesc	fc_tupdesc;
	AttInMetadata *fc_attinmeta;

	if (SRF_IS_FIRSTCALL())
	{
		text	   *fc_data = PG_GETARG_TEXT_PP(0);
		int			fc_res;
		MemoryContext fc_oldcontext;

		fc_funcctx = SRF_FIRSTCALL_INIT();

		/* 我们需要在多次调用上下文中分配状态 */ 
		fc_oldcontext = MemoryContextSwitchTo(fc_funcctx->multi_call_memory_ctx);

		/* 为我们的结果类型构建一个元组描述符 */
		if (get_call_result_type(fcinfo, NULL, &fc_tupdesc) != TYPEFUNC_COMPOSITE)
			elog(ERROR, "return type must be a row type");

		fc_attinmeta = TupleDescGetAttInMetadata(fc_tupdesc);
		fc_funcctx->attinmeta = fc_attinmeta;

		fc_state = (pgp_armor_headers_state *) palloc(sizeof(pgp_armor_headers_state));

		fc_res = pgp_extract_armor_headers((uint8 *) VARDATA_ANY(fc_data),
										VARSIZE_ANY_EXHDR(fc_data),
										&fc_state->nheaders, &fc_state->keys,
										&fc_state->values);
		if (fc_res < 0)
			px_THROW_ERROR(fc_res);

		MemoryContextSwitchTo(fc_oldcontext);
		fc_funcctx->user_fctx = fc_state;
	}

	fc_funcctx = SRF_PERCALL_SETUP();
	fc_state = (pgp_armor_headers_state *) fc_funcctx->user_fctx;

	if (fc_funcctx->call_cntr >= fc_state->nheaders)
		SRF_RETURN_DONE(fc_funcctx);
	else
	{
		char	   *fc_values[2];

		/* 我们假设键（和值）是 UTF-8 编码。 */ 
		fc_utf8key = fc_state->keys[fc_funcctx->call_cntr];
		fc_utf8val = fc_state->values[fc_funcctx->call_cntr];

		fc_values[0] = pg_any_to_server(fc_utf8key, strlen(fc_utf8key), PG_UTF8);
		fc_values[1] = pg_any_to_server(fc_utf8val, strlen(fc_utf8val), PG_UTF8);

		/* 构建一个元组 */ 
		fc_tuple = BuildTupleFromCStrings(fc_funcctx->attinmeta, fc_values);
		SRF_RETURN_NEXT(fc_funcctx, HeapTupleGetDatum(fc_tuple));
	}
}



/* 
 * PGP 密钥 ID 的封装 
 */ 

Datum pgp_key_id_w(PG_FUNCTION_ARGS)
{
	bytea	   *fc_data;
	text	   *fc_res;
	int			fc_res_len;
	MBuf	   *fc_buf;

	fc_data = PG_GETARG_BYTEA_PP(0);
	fc_buf = fc_create_mbuf_from_vardata(fc_data);
	fc_res = palloc(VARHDRSZ + 17);

	fc_res_len = pgp_get_keyid(fc_buf, VARDATA(fc_res));
	mbuf_free(fc_buf);
	if (fc_res_len < 0)
		px_THROW_ERROR(fc_res_len);
	SET_VARSIZE(fc_res, VARHDRSZ + fc_res_len);

	PG_FREE_IF_COPY(fc_data, 0);
	PG_RETURN_TEXT_P(fc_res);
}
